package utils;

import com.google.common.collect.Lists;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Function;

public class ImportExcelUtils {
    private static final Logger LOGGER = LoggerFactory.getLogger(ImportExcelUtils.class);

    private static final Integer EXCEL_ROW_LENGTH = Integer.MAX_VALUE - 10;

    private static final String XLS = "xls";

    private static final String XLSX = "xlsx";

    private static final List<String> fileExts = Lists.newArrayList(XLS, XLSX);

    /**
     * Excel导入
     *
     * @param startIndex    起始行
     * @param multipartFile 文件
     * @param function      单行操作
     * @param <T>           实体类
     */
    public static <T> List<T> importExcel(Integer startIndex, MultipartFile multipartFile, Function<Row, Optional<T>> function) {
        Workbook workbook = null;
        try {
            workbook = readWorkbook(multipartFile);
        } catch (IOException | InvalidFormatException e) {
            LOGGER.info("importExcel:{}", e.getMessage());
            e.printStackTrace();
        }
        if (Objects.isNull(workbook)) {
            LOGGER.info("importExcel.workbook is null!");
            return Lists.newArrayList();
        }
        return importExcel(startIndex, workbook, function);
    }


    /**
     * Excel导入
     *
     * @param file     文件
     * @param function 单行操作
     * @param <T>      实体类
     */
    public static <T> List<T> importExcel(Integer startIndex, File file, Function<Row, Optional<T>> function) {
        Workbook workbook = null;
        try {
            workbook = fileToWorkbook(file);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return importExcel(startIndex, workbook, function);
    }


    private static <T> List<T> importExcel(Integer startIndex, Workbook workbook, Function<Row, Optional<T>> function) {
        Sheet sheet = Objects.requireNonNull(workbook).getSheetAt(0);
        int rowCount = sheet.getPhysicalNumberOfRows();
        List<T> result = Lists.newArrayList();
        for (int i = startIndex; i < rowCount; i++) {
            if (result.size() > EXCEL_ROW_LENGTH) {
                return result;
            }
            Row row = sheet.getRow(i);
            if (Objects.isNull(row)) {
                return result;
            }
            Optional<T> apply = function.apply(row);
            if (apply.isPresent()) {
                result.add(apply.get());
            } else {
                break;
            }
        }
        return result;
    }


    private static Workbook readWorkbook(MultipartFile multipartFile) throws IOException, InvalidFormatException {
        verifyFileExtension(multipartFile);
        return multipartFileToWorkbook(multipartFile);
    }

    private static void verifyFileExtension(MultipartFile multipartFile) throws InvalidFormatException {
        if (!isExcelExtension(multipartFile.getOriginalFilename())) {
            LOGGER.info("verifyFileExtension.originalFileName:{}", multipartFile.getOriginalFilename());
            LOGGER.info("verifyFileExtension.name:{}", multipartFile.getName());
            throw new InvalidFormatException("This file extension is not verify");
        }
    }

    private static boolean isExcelExtension(String fileName) {
        String toLowerCase = fileName.toLowerCase();
        for (String fileExt : fileExts) {
            if (toLowerCase.contains(fileExt)) {
                return true;
            }
        }
        return false;
    }

    private static boolean isExcelXls(String fileName) {
        fileName = fileName.toLowerCase();
        return fileName.endsWith(XLS);
    }

    private static boolean isExcelXlsx(String fileName) {
        fileName = fileName.toLowerCase();
        return fileName.endsWith(XLSX);
    }

    private static Workbook multipartFileToWorkbook(MultipartFile multipartFile) throws IOException {
        if (isExcelXls(multipartFile.getOriginalFilename())) {
            return new HSSFWorkbook(multipartFile.getInputStream());
        } else {
            return new XSSFWorkbook(multipartFile.getInputStream());
        }
    }

    private static Workbook fileToWorkbook(File file) throws IOException {
        InputStream inputStream = new FileInputStream(file);
        if (isExcelXls(file.getName())) {
            return new HSSFWorkbook(inputStream);
        } else {
            return new XSSFWorkbook(inputStream);
        }
    }

}
